package com.ywyself.plugin.spider;

import com.sun.istack.internal.NotNull;
import com.ywyself.ext.utils.YsStringUtils;
import com.ywyself.plugin.spider.uils.DataUtils;
import org.junit.Test;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

public class SpiderUtils {
    private static String fileBasePath = File.separator + "data" + File.separator + "xiaoshuo" + File.separator;
    //    private static String fileBasePath = "D:" + File.separator + "xiaoshuo" + File.separator;
    private static String mergeFilePre = "0merge_";

    /**
     * 列出章节
     *
     * @param cateUrl 目录页面地址
     * @return List&lt;String[]{章节名称,章节内容}&gt;
     * @throws ClassNotFoundException    没有找到类
     * @throws NoSuchMethodException     没有找到方法
     * @throws IllegalAccessException    不合法的访问
     * @throws InstantiationException    实例化失败
     * @throws InvocationTargetException 反射调用的方法和构造器中出现的异常导致
     */
    private static List<String[]> listCate(String className, String cateUrl) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> c = Class.forName("com.ywyself.plugin.spider.impl." + className);
        Method listCate = c.getMethod("listCate", String.class);
        return (List<String[]>) listCate.invoke(c.newInstance(), new String[]{cateUrl});
    }

    /**
     * 读取章节内容
     *
     * @param conUrl 具体章节地址
     * @return String[]{章节名称, 章节内容}
     * @throws ClassNotFoundException    没有找到类
     * @throws NoSuchMethodException     没有找到方法
     * @throws IllegalAccessException    不合法的访问
     * @throws InstantiationException    实例化失败
     * @throws InvocationTargetException 反射调用的方法和构造器中出现的异常导致
     */
    private static String[] getContent(String className, String conUrl) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        Class<?> c = Class.forName("com.ywyself.plugin.spider.impl." + className);
        Method getContent = c.getMethod("getContent", String.class);
        return (String[]) getContent.invoke(c.newInstance(), conUrl);
    }

    @Test
    public void testM() {
        try {
            merge("duyidu圣墟");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testS() {
        try {
            save("Du1Du", "http://du1du.org", "http://du1du.org/txt-32542/mulu.htm", "duyidu圣墟", ""); // 第二十六章
        } catch (IOException | ClassNotFoundException | InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
    }

    /**
     * 列出正在运行的任务
     *
     * @return id, name, priority, isAlive, isInterrupted
     */
    public static List<Map<String, Object>> listRunningThread() {
        ThreadGroup group = Thread.currentThread().getThreadGroup();
        List<Map<String, Object>> list = new ArrayList<>();
        if (group == null) {
            return null;
        }
        Thread[] threads = new Thread[(int) (group.activeCount() * 1.2)];
        group.enumerate(threads, true);
        Map<String, Object> info;
        for (Thread t : threads) {
            if (t == null || !t.getName().startsWith("spider")) {
                continue;
            }
            info = new HashMap<>();
            info.put("id", t.getId());
            info.put("name", t.getName());
            info.put("isAlive", t.isAlive());
            info.put("isInterrupted", t.isInterrupted());
            list.add(info);
        }
        return list;
    }

    /**
     * 列出已经爬取的文件存放的文件名
     *
     * @return 文件夹list
     */
    public static List<String> listFold() {
        File dir = new File(fileBasePath);
        if (!dir.exists()) {
            return null;
        }
        File[] files = dir.listFiles();
        if (files == null || files.length == 0) {
            return null;
        }
        // 按最后修改时间倒序
        Arrays.sort(files, (o1, o2) -> (int) (o2.lastModified() - o1.lastModified()));
        List<String> list = new ArrayList<>(files.length);
        for (File f : files) {
            list.add(f.getName());
        }
        if (list.size() == 0) {
            return null;
        } else {
            return list;
        }
    }

    /**
     * 获取合并后的文件，没有则返回null
     *
     * @param foldName 文件夹名
     * @return 合并了的文件
     */
    public static File getMergeFile(@NotNull String foldName) {
        File file = new File(fileBasePath + foldName + File.separator);
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            return null;
        }
        Arrays.sort(files, Comparator.comparing(File::getName));
        for (int i = files.length - 1; i >= 0; i--) {
            if (files[i].getName().contains(mergeFilePre)) {
                if (files[i].isFile()) {
                    return files[i];
                }
            }
        }
        return null;
    }

    /**
     * 合并文件
     *
     * @param foldName 文件夹名
     * @throws IOException IO错误
     */
    public static void merge(@NotNull String foldName) throws IOException {
        mergeFile(foldName.trim());
    }

    /**
     * @param className    类文件名
     * @param rootUrl      网站地址
     * @param cateUrl      目录地址
     * @param startChapter 从哪个章节开始下载，为空则从第一个开始
     * @throws IOException               IO错误
     * @throws ClassNotFoundException    没有找到类
     * @throws NoSuchMethodException     没有找到方法
     * @throws InvocationTargetException 反射调用的方法和构造器中出现的异常导致
     * @throws InstantiationException    实例化失败
     * @throws IllegalAccessException    不合法的访问
     */
    public static void save(@NotNull String className, @NotNull String rootUrl, @NotNull String cateUrl, @NotNull String foldName, String startChapter) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        className = className.trim();
        rootUrl = rootUrl.trim();
        if (rootUrl.endsWith("/")) {
            rootUrl = rootUrl.substring(0, rootUrl.length() - 1);
        }
        cateUrl = cateUrl.trim();
        foldName = foldName.trim();
        Random random = new Random();
        if (YsStringUtils.isBlank(startChapter)) {
            startChapter = "";
        } else {
            startChapter = startChapter.trim();
        }
        System.out.println("扫描目录...");
        List<String[]> links = listCate(className, cateUrl);
        System.out.println("扫描完成");
        if (links == null || links.size() == 0) {
            System.out.println();
            System.out.println("没有扫描到章节");
            return;
        }
        /*
          这里是进行处理从哪个章节开始下载，把头章节之前的元素移除，不进行下载，并标记为跳过了章节
         */
        // 是否跳过了章节，如果跳过了，就不进行文件删除
        boolean isSkiped = false;
        if (startChapter.length() > 0) {
            Iterator<String[]> iterator = links.iterator();
            while (iterator.hasNext()) {
                String[] e = iterator.next();
                if (e[0].contains(startChapter)) {
                    break;
                } else {
                    isSkiped = true;
                    // 是否有跳过章节，如果有则移除list之前的元素
                    iterator.remove();
                }
            }
        }
        /*
         * 检查文件夹是否存在，不存在则创建文件夹
         */
        String path = fileBasePath + foldName + File.separator;
        File dir = new File(path);
        if (!dir.exists()) {
            System.out.println("创建文件夹");
            dir.mkdirs();
            System.out.println("创建完成");
        } else {
            // 如果跳过了章节，就不进行文件的删除操作
            if (!isSkiped) {
                File[] files = dir.listFiles();
                if (files != null)
                    for (File f : files) f.delete();
            }
        }
        System.out.println();
        System.out.println("开始下载章节");
        for (String[] chapter : links) {

            int sec = random.nextInt(10) + 2;
            System.out.println("等待" + sec + "s后执行");
            try {
                Thread.sleep(1000 * sec);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("正在下载... " + chapter[0]);
            String[] res = getContent(className, rootUrl + chapter[1]);
            if (res == null || res[0].trim().length() == 0) {
                System.out.println("下载失败");
                System.out.println("章节地址" + rootUrl + chapter[1]);
                continue;
            }
            System.out.println("下载完成");

            System.out.println("正在保存...");
            String fileName = path + DataUtils.getPrefix() + ".txt";
            FileWriter fw = new FileWriter(fileName);
            fw.append(DataUtils.title(res[0]));
            fw.append("\r\n\r\n");
            fw.append(DataUtils.content(res[1]));
            fw.close();
            System.out.println("保存完成");

            System.out.println("版式处理");
            File old = new File(fileName);
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(old)));
            FileWriter fw2 = new FileWriter(path + DataUtils.getPrefix() + "_" + DataUtils.title(chapter[0]) + ".txt");
            String line;
            while ((line = br.readLine()) != null) {
                line = DataUtils.line(line);
                if (line.length() == 0) continue;
                fw2.append(line).append("\r\n\r\n");
            }
            fw2.close();
            br.close();
            old.delete();
            System.out.println("处理完成");
        }
        System.out.println();
        System.out.println("全部章节下载完成");
        System.out.println();
        mergeFile(foldName);
    }

    /**
     * 文件合并
     *
     * @throws IOException IO 错误
     */
    private static void mergeFile(@NotNull String foldName) throws IOException {
        File file = new File(fileBasePath + foldName + File.separator);
        File[] files = file.listFiles();
        if (files == null || files.length == 0) {
            System.out.println("没有文件需要合并");
            return;
        }
        for (File e : files) {
            if (e.isFile() && e.getName().startsWith(mergeFilePre)) e.delete();
        }
        Arrays.sort(files, Comparator.comparing(File::getName));
        boolean isTitle;
        System.out.println("开始合并");
        FileWriter fw = new FileWriter(fileBasePath + foldName + File.separator + mergeFilePre + file.getName() + "_" + DataUtils.getPrefix() + ".txt");
        for (File e : files) {
            if (!e.isFile()) continue;
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(e.getAbsolutePath())));
            String line;
            isTitle = true;
            while ((line = br.readLine()) != null) {
                line = DataUtils.line(line);
                if (line.length() == 0) continue;
                if (isTitle) {
                    fw.append("\r\n");
                    isTitle = false;
                }
                fw.append("\r\n").append(line).append("\r\n");
            }
            br.close();
        }
        System.out.println("合并完成");
        fw.close();
    }
}
